import assert from 'assert';

const defaultEntity = {//树表实体key名标识
    ID: 'id', //主键id
    PID: 'parent_id',//父级id
    PIDS: 'parent_ids', //父级ids
    LEVEL: 'level',//级别
    CHILDREN: 'children',
    LEAF: 'tree_leaf',//是否是叶子节点
    TREE_NAMES: 'tree_names',//全路径名称
    TREE_NAME: 'tree_name', //节点名称
    DELETE_WAY: 1, //删除方式：0修改状态，不删除数据;1.删除数据;
    STATUS: 'status'//状态

};

/**
 *  Tree基类
 */
class TreeService extends think.Service {
    constructor(config) {
        super();
        Object.assign(defaultEntity, config);
    }

    /**
     * 用于树表新增和更新当前节点的所有pids,level,pid,以及所有子子孙孙的pids,　支持任意一级的更新，支持父子节点交换。
     * @param tableName  表名 string
     * @param parentEntity 父对象实体 Object
     * @param childEntity 要保存或更新的子对象实体 Object
     * @param options 选项对象
     */
    async saveTree(tableName, parentEntity, childEntity, options) {
        assert(typeof tableName === 'string', 'tableName must be a string');
        assert(parentEntity instanceof Object, 'parentEntity must be a Object');
        assert(childEntity instanceof Object, 'childEntity must be a Object');
        const tableModel = this.db[tableName];
        const ID = defaultEntity.ID;
        const PID = defaultEntity.PID;
        const PIDS = defaultEntity.PIDS;
        const LEVEL = defaultEntity.LEVEL;
        const TREE_NAMES = defaultEntity.TREE_NAMES;
        const TREE_NAME = defaultEntity.TREE_NAME;
        const TREE_LEAF = defaultEntity.TREE_LEAF;
        //获取父对象和子对象属性
        const {[ID]: pEntityId, [PID]: pEntityPid, [PIDS]: pEntityPids, [TREE_NAMES]: pEntityTreeNames = ''} = parentEntity;
        const {[ID]: cEntityId, [PID]: cEntityPid, [PIDS]: cEntityPids, [TREE_NAME]: cEntityTreeName = ''} = childEntity;
        // 获取子对象修改前的parentIds和pId，用于更新子节点的parentIds和Pid
        const oldParentIds = cEntityPids;
        const oldParentId = cEntityPid;

        // 如果目标父节点是自己，则不处理PIDS、PID、LEVEL
        if (pEntityId === cEntityId) {
            delete childEntity[PID];
            delete childEntity[PIDS];
            delete childEntity[LEVEL];
            return tableModel.upsert(childEntity);
        }
        // 如果没有设置父节点，则代表为根节点
        if (pEntityPid === null || pEntityPid === '' || pEntityPid === undefined) {
            childEntity[PID] = 0;
            childEntity[PIDS] = '0,';
        } else {
            //获取父对象实体的parent_ids并且拼接parent_ids
            let updateParentIds = '';
            //如果存在父子节点交换，踢出在树路径里自己的id
            if (pEntityPids && pEntityPids.includes(cEntityId)) {
                //更新当前节点的parentIds
                updateParentIds = `${pEntityPids}${pEntityId},`.replace(`${cEntityId},`, '');
            } else {
                updateParentIds = `${pEntityPids}${pEntityId},`;
            }

            childEntity[PIDS] = updateParentIds;
            childEntity[PID] = pEntityId;
            childEntity[LEVEL] = updateParentIds.split(',').length - 1;
        }
        if (cEntityTreeName) {
            //构造节点全路径名称
            pEntityTreeNames ? childEntity[TREE_NAMES] = pEntityTreeNames + '/' + cEntityTreeName : childEntity[TREE_NAMES] = cEntityTreeName;
        }

        //如果是新增操作,设置当前节点为末级节点，同时设置父节点为非末级节点
        if (!cEntityId) {
            childEntity[TREE_LEAF] = 1;
            tableModel.update({[TREE_LEAF]: 0}, {
                where: {
                    [ID]: pEntityId
                }
            });
        }
        //保存或更新当前实体
        const upsertResult = tableModel.upsert(childEntity);

        //更新当前修改节点下所有子节点的parentIds
        if (cEntityId && pEntityPids) {
            assert(oldParentIds != undefined, 'parentIds must be not empty');
            let sql = `SELECT ${ID},${PID},${PIDS} FROM ${tableName} WHERE ${PIDS} like '%${oldParentIds}${cEntityId},%'`;
            const list = await this.CrudService.querySql(sql);
            if (list.length > 0) {
                for (let value of list) {
                    if (value[PIDS] && oldParentIds) {
                        let obj = {};
                        //如果存在父子节点交换,踢出在树路径里自己的id
                        if (pEntityPids.includes(cEntityId)) {
                            obj[PIDS] = value[PIDS].replace(`${cEntityId},`, '');
                            //更新交换节点前的pid
                            if (value[PID] === cEntityId) {
                                obj[PID] = oldParentId;
                            }

                        } else {
                            obj[PIDS] = value[PIDS].replace(oldParentIds, cEntityPids);
                        }
                        obj[LEVEL] = obj[PIDS].split(',').length - 1;
                        tableModel.update(obj, {
                            where: {
                                id: value[ID]
                            }
                        });


                    }

                }

            }

        }
        return upsertResult;


    }

    /**
     *  全表扫描，用于重新生成全表的PIDS
     *
     * @param tableName  表名 string
     */
    async setAllParentIds(tableName) {

        assert(typeof tableName === 'string', 'tableName must be a string');
        const tableModel = this.db[tableName];
        //获得表中所有 数据
        let tableDataList = await  tableModel.findAll();
        //将数组构造为id为key的对象
        let idField = defaultEntity.ID;
        let parentIDField = defaultEntity.PID;
        let parentIDSField = defaultEntity.PIDS;
        let dataObj = {};
        tableDataList.map(item => {
            dataObj[item[idField]] = item.dataValues;
        });
        //遍历数组，并查出parent_ids
        tableDataList.map(item => {
            let parent_ids = item[parentIDField] + ',';
            let isHasParent = true;
            let parent_id = item[parentIDField];
            while (isHasParent) {
                let parentObj = {};
                parentObj = dataObj[parent_id];
                while (parentObj != undefined) {
                    parent_ids = parentObj.parent_id + ',' + parent_ids;
                    parentObj = dataObj[parentObj.parent_id];
                }
                isHasParent = false;
            }
            item[parentIDSField] = parent_ids
            return item
        });
        //捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
        try {
            for (let i = 0; i < tableDataList.length; i++) {
                await  tableModel.update({[parentIDSField]: tableDataList[i].dataValues[parentIDSField]}, {where: {[idField]: tableDataList[i][idField]}});
            }
        } catch (e) {
            console.log(e)
        }


    }

    /**
     *  全表扫描，用于重新生成全表的LEVEL
     *
     * @param tableName  表名 string
     */
    async setLevel(tableName) {
        assert(typeof tableName === 'string', 'tableName must be a string');
        const tableModel = this.db[tableName];
        //获得表中所有 数据
        let tableDataList = await  tableModel.findAll();
        let idField = defaultEntity.ID;
        let parentIDSField = defaultEntity.PIDS;
        let levelField = defaultEntity.LEVEL;
        //遍历数组，并查出parent_ids
        tableDataList.map(item => {
            let parent_idsArr = item[parentIDSField].split(',');
            item[levelField] = parent_idsArr.length - 1

        });
        //捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
        try {
            for (let i = 0; i < tableDataList.length; i++) {
                await  tableModel.update({[levelField]: tableDataList[i][levelField]}, {where: {[idField]: tableDataList[i][idField]}});
            }
        } catch (e) {
            console.log(e)
        }
    }

    /**
     * 格式转树状结构
     * @param   {Array}  a 原数据
     */
    transData(a) {
        let r = [],
            hash = {},
            id = defaultEntity.ID,
            pid = defaultEntity.PID,
            children = defaultEntity.CHILDREN,
            i = 0,
            j = 0,
            len = a.length;
        for (; i < len; i++) {
            hash[a[i][id]] = a[i];
        }
        for (; j < len; j++) {
            var aVal = a[j],
                hashVP = hash[aVal[pid]];
            if (hashVP) {
                !hashVP[children] && (hashVP[children] = []);
                hashVP[children].push(aVal);
            } else {
                r.push(aVal);
            }
        }
        return r;
    }

    /**
     * 根据表名和ID获取当前节点的子子孙孙
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
    findGrandchildrens(tableName, id, fields = ['*']) {
        const {ID, PIDS} = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%')`;
        return this.CrudService.querySql(sql);
    }

    /**
     * 根据表名和ID获取当前节点的子子孙孙（包含自己）
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
    findGrandchildrensAndSelf(tableName, id, fields = ['*']) {
        const {ID, PIDS} = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%') or ${ID}=${id}`;
        return this.CrudService.querySql(sql);
    }

    /**
     * 根据表名和ID获取当前节点下的所有子节点(不包含孙孙子子)
     */
    findChildrens(tableName, id, fields = ['*']) {
        const {ID, PIDS, PID} = defaultEntity;
        fields = fields.join();
        const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PID}=${id}`;
        return this.CrudService.querySql(sql);

    }

    /**
     * 根据表名和ID删除当前节点及其下面的所有子节点
     */
    async deleteParentAndChildrenNode(tableName, id) {
        assert(typeof tableName === 'string', 'tableName must be a string');
        const tableModel = this.db[tableName];
        const {ID, PIDS, DELETE_WAY, STATUS} = defaultEntity;
        const Op = this.db.sequelize.Op;
        try {
            if (DELETE_WAY) {
                await tableModel.destroy({
                    where: {
                        [Op.or]: {
                            [PIDS]: {
                                [Op.like]: '%' + id + ','
                            },
                            [ID]: id
                        }
                    }
                });
            } else {
                await tableModel.update({[STATUS]: 1}, {
                    where: {
                        [Op.or]: {
                            [PIDS]: {
                                [Op.like]: '%' + id + ','
                            },
                            [ID]: id
                        }
                    }
                });
            }
        } catch (error) {
            return this
                .exceptionService
                .handleError(error);
        }
    }
}

export default TreeService;
